package model.laberinto;

import java.util.ArrayList;

import model.interfaces.ICaminos;
import model.utilidades.Vector;

public class Caminos implements ICaminos{
	protected ArrayList<Camino> caminos;
	
	public Caminos(){
		caminos = new ArrayList<Camino>();
	}

	public void agregarCamino(Vector punto1,Vector punto2){
		if(punto1.alineadoCon(punto2))
				caminos.add(new Camino(punto1, punto2));
	}
	
	@Override
	public ArrayList<Camino> obtenerCaminos() {
		return caminos;
	}
	
	public Vector obtenerPosicionFinal(Vector pos, Vector vel){
		Vector destino = pos.sumarleElVector(vel);
		
		ArrayList<Camino> caminosQueContienenAlPunto = obtenerCaminosQueContienen(pos);

		if(caminosQueContienenAlPunto.size() == 1){
			Camino camino = caminosQueContienenAlPunto.get(0);
			if(camino.estaDentroDelCamino(destino))
				return destino;
			if(camino.estaAlineadoCon(destino))
				return camino.truncarPosicion(destino);
		}else{
			for(Camino camino:caminosQueContienenAlPunto){
				if(camino.estaDentroDelCamino(destino)){
					return destino;
				}
			}
		}

		return pos;
	}
	
	public ArrayList<Vector> obtenerDireccionesPosibles(Vector pos){
		ArrayList<Vector> direccionesPosibles = new ArrayList<Vector>();
		
		ArrayList<Camino> caminosQueContienenAlPunto = obtenerCaminosQueContienen(pos);
		for(Camino camino:caminosQueContienenAlPunto){
			direccionesPosibles.addAll(camino.obtenerDireccionesPosibles(pos));
		}
		return direccionesPosibles;	
	}
	
	protected ArrayList<Camino> obtenerCaminosQueContienen(Vector punto){
		ArrayList<Camino> caminosQueContienenAlPunto = new ArrayList<Camino>();
		for(Camino camino:caminos){
			if(camino.estaDentroDelCamino(punto))
				caminosQueContienenAlPunto.add(camino);
		}
		return caminosQueContienenAlPunto;
	}
	
	public boolean esUnBorde(Vector posicion){
		for(Camino camino:caminos){
			if(camino.esUnBorde(posicion))
					return true;
		}
		return false;
	}
	
	public class Camino{
		private Vector extremo1;
		private Vector extremo2;
		private boolean esCaminoHorizontal;
		private int xExtremo1;
		private int yExtremo1;
		private int xExtremo2;
		private int yExtremo2;

		public Camino(Vector extremo1,Vector extremo2){
			this.extremo1 = extremo1;
			this.extremo2 = extremo2;
			
			this.xExtremo1 = extremo1.obtenerCoordenadaX();
			this.yExtremo1 = extremo1.obtenerCoordenadaY();

			this.xExtremo2 = extremo2.obtenerCoordenadaX();
			this.yExtremo2 = extremo2.obtenerCoordenadaY();
			
			esCaminoHorizontal = extremo1.obtenerCoordenadaY() == extremo2.obtenerCoordenadaY();
		}
		
		public boolean esUnBorde(Vector posicion) {
			return posicion.esIgualA(extremo1) || posicion.esIgualA(extremo2);
		}

		public Vector obtenerPunto1(){
			return extremo1;
		}

		public Vector obtenerPunto2(){
			return extremo2;
		}		
		
		public ArrayList<Vector> obtenerDireccionesPosibles(Vector ubicacion){
			ArrayList<Vector> DireccionesPosibles = new ArrayList<Vector>();

			boolean ubicacionNoEstaEnExtremos = !esUnBorde(ubicacion);
			
			if(esCaminoHorizontal){

				int x_ubicacion = ubicacion.obtenerCoordenadaX();
				
				boolean ubicacionEstaEnExtremo1 = (xExtremo1 == x_ubicacion);
				boolean ubicacionEstaEnExtremo2 = (xExtremo2 == x_ubicacion);
				
				boolean extremo2EsInicioDeCamino = xExtremo1 > xExtremo2;
				boolean extremo1EsInicioDeCamino = xExtremo1 <= xExtremo2;
				
				boolean condicion1 = ubicacionEstaEnExtremo1 && extremo2EsInicioDeCamino;
				boolean condicion2 = ubicacionEstaEnExtremo2 && extremo1EsInicioDeCamino;
				boolean ubicacionEstaEnFinalDeCamino = condicion1 || condicion2;
		
				boolean condicion3 = ubicacionEstaEnExtremo1 && extremo1EsInicioDeCamino;
				boolean condicion4 = ubicacionEstaEnExtremo2 && extremo2EsInicioDeCamino;		
				boolean ubicacionEstaEnInicioDeCamino = condicion3 || condicion4;

				
				if(ubicacionEstaEnFinalDeCamino || ubicacionNoEstaEnExtremos)
					DireccionesPosibles.add(new Vector(-1,0));
				
				if(ubicacionEstaEnInicioDeCamino || ubicacionNoEstaEnExtremos)
					DireccionesPosibles.add(new Vector(1,0));
					
			}else{

				int y_ubicacion = ubicacion.obtenerCoordenadaY();
				
				boolean ubicacionEstaEnExtremo1 = (yExtremo1 == y_ubicacion);
				boolean ubicacionEstaEnExtremo2 = (yExtremo2 == y_ubicacion);
				
				boolean extremo2EsInicioDeCamino = yExtremo1 > yExtremo2;
				boolean extremo1EsInicioDeCamino = yExtremo1 <= yExtremo2;
				
				boolean condicion1 = (ubicacionEstaEnExtremo1 && extremo2EsInicioDeCamino);
				boolean condicion2 = (ubicacionEstaEnExtremo2 && extremo1EsInicioDeCamino);
				boolean ubicacionEstaEnFinalDeCamino = condicion1 || condicion2;

				boolean condicion3 = ubicacionEstaEnExtremo1 && extremo1EsInicioDeCamino;
				boolean condicion4 = ubicacionEstaEnExtremo2 && extremo2EsInicioDeCamino;		
				boolean ubicacionEstaEnInicioDeCamino = condicion3 || condicion4;
				
				
				if(ubicacionEstaEnFinalDeCamino || ubicacionNoEstaEnExtremos)
					DireccionesPosibles.add(new Vector(0,1));
				
				
				if(ubicacionEstaEnInicioDeCamino || ubicacionNoEstaEnExtremos)
					DireccionesPosibles.add(new Vector(0,-1));
				
			}
			
			return DireccionesPosibles;
		}
		
		public Vector truncarPosicion(Vector ubicacion){
			
			if(esCaminoHorizontal){

				int x_ubicacion = ubicacion.obtenerCoordenadaX();

				if(xExtremo1 > xExtremo2){
					if(x_ubicacion >= xExtremo1)
						return extremo1; //si xExtremo1 > xExtremo2 && x_posicionAbsolutaDePantalla >= xExtremo1
					else
						return extremo2; //si xExtremo1 > xExtremo2 && x_posicionAbsolutaDePantalla < xExtremo1
				}else{
					if(x_ubicacion >= xExtremo2)
						return extremo2; //si xExtremo1 <= xExtremo2 && x_posicionAbsolutaDePantalla >= xExtremo1
					else
						return extremo1; //si xExtremo1 <= xExtremo2 && x_posicionAbsolutaDePantalla < xExtremo1	
				}
				
			}else{

				int y_ubicacion = ubicacion.obtenerCoordenadaY();

				if(yExtremo1 > yExtremo2){
					if(y_ubicacion >= yExtremo1)
						return extremo1; //si yExtremo1 > yExtremo2 && y_posicionAbsolutaDePantalla >= yExtremo1
					else
						return extremo2; // yExtremo1 > yExtremo2 && y_posicionAbsolutaDePantalla < yExtremo1
				}else{
					if(y_ubicacion >= yExtremo2)
						return extremo2; //si yExtremo1 <= yExtremo2 && y_posicionAbsolutaDePantalla >= yExtremo1
					else
						return extremo1; //si yExtremo1 <= yExtremo2 && y_posicionAbsolutaDePantalla < yExtremo1	 				
				}
			}
		}
		
		public boolean estaAlineadoCon(Vector ubicacion){
			
			int x_ubicacion = ubicacion.obtenerCoordenadaX();
			int y_ubicacion = ubicacion.obtenerCoordenadaY();
			
			boolean alineadoACaminoHorizontal=(esCaminoHorizontal && (yExtremo1 == y_ubicacion));
			boolean alineadoACaminoVertical=(!esCaminoHorizontal && (xExtremo1 == x_ubicacion));
			
			return alineadoACaminoHorizontal || alineadoACaminoVertical;
		}
		
		public boolean estaDentroDelCamino(Vector ubicacion){

			int x_ubicacion = ubicacion.obtenerCoordenadaX();
			int y_ubicacion = ubicacion.obtenerCoordenadaY();
			
			if(esCaminoHorizontal && (y_ubicacion == yExtremo1)){

				boolean condicion1 = (xExtremo1 >= x_ubicacion) && (xExtremo2 <= x_ubicacion);
				boolean condicion2 = (xExtremo1 <= x_ubicacion) && (xExtremo2 >= x_ubicacion);
								
				boolean estaEntreLosDos = condicion1 || condicion2;
				return estaEntreLosDos;
			}

			if(!esCaminoHorizontal && (x_ubicacion == xExtremo1)){
				
				boolean condicion1 = (yExtremo1 >= y_ubicacion) && (yExtremo2 <= y_ubicacion);
				boolean condicion2 = (yExtremo1 <= y_ubicacion) && (yExtremo2 >= y_ubicacion);
				
				boolean estaEntreLosDos = condicion1 || condicion2;
				return estaEntreLosDos;
			}
			
			return false;
		}
	}
}
